package poset;

import java.util.ArrayList;
import java.util.TreeMap;
import java.util.TreeSet;

import spaces.ChargeableVectorNode;
import spaces.VectorNode;
import spaces.VectorSpace;
import vectors.Vector;

public class RectangularPosetSpace extends PosetSpace{
	
	private static final double FIX_FACTOR = 0.001;
	private static final double BUFFER = 1;
	private final int dims;
	
	public RectangularPosetSpace(String relation, int dims){
		this(relation, new ArrayList<User>(), dims);
	}
	
	public RectangularPosetSpace(String relation, ArrayList<User> users, int dims){
		super(relation, users);
		this.dims = dims;
	}

	protected TreeMap<Item,Vector> getDifferences(User user) {
		TreeMap<Item,Vector> diffs = new TreeMap<Item,Vector>();
		for(Item item : user.getPoset(relation).getItems()){
			diffs.put(item, item.getPos().minus(user.getPos()));
		}
		return diffs;
	}

	public int getDims() {
		return dims;
	}

	public double getMin(int dim) {
		if(!isInitialized){
			throw new IllegalStateException("Cannot call getMin until initialized.");
		}
		double min = Double.MAX_VALUE;
		double curVal;
		for(VectorNode node : space.getNodes()){
			curVal = node.getPos().getValue(dim);
			min = Math.min(min, curVal);
		}
		return min;
	}
	
	public double getMax(int dim) {
		if(!isInitialized){
			throw new IllegalStateException("Cannot call getMax until initialized.");
		}
		double max = Double.MIN_VALUE;
		double curVal;
		for(VectorNode node : space.getNodes()){
			curVal = node.getPos().getValue(dim);
			max = Math.max(max, curVal);
		}
		return max;
	}

	public double getTension() {
		return tension;
	}
	
	public VectorSpace getVectorSpace() {
		if(!isInitialized){
			throw new IllegalStateException("Cannot call getNodeSpace until initialized.");
		}
		return (VectorSpace)space;
	}

	@Override
	protected VectorSpace createSpace(TreeSet<VectorNode> nodes) {
		return new VectorSpace(dims, nodes);
	}

	@Override
	public double getBuffer() {
		return BUFFER;
	}

	@Override
	protected void iterate(User user) {
		// Get User-to-Item Vectors from 
		TreeMap<Item,Vector> diffs = getDifferences(user);
		
		// Extract Distances
		TreeMap<Item,Double> dists = new TreeMap<Item,Double>();
		for(Item item : diffs.keySet()){
			dists.put(item, diffs.get(item).getLengthSq());
		}
		
		// Create Poset from distances
		Poset realRelOrder;
		realRelOrder = createPosetFromDistances(dists);
		
		// Find User Orderings that are incorrect
		ArrayList<Ordering> wrongOrders = user.getPoset(relation).getUnsatisfiedExplicitOrderings(realRelOrder);
		for(Ordering wo : wrongOrders){
			
			// Find amount incorrect
			Vector toItem1 = diffs.get(wo.getItem1());
			Vector toItem2 = diffs.get(wo.getItem2());
			double error = toItem1.getLength() - toItem2.getLength() + BUFFER;
			tension+=error;
			
			// Add charges to correct
			Vector error1 = toItem1.normalize().times(error*FIX_FACTOR);
			wo.getItem1().addCharge(error1.times(-1));
			user.addCharge(error1);
			
			Vector error2 = toItem2.normalize().times(error*FIX_FACTOR);
			wo.getItem2().addCharge(error2);
			user.addCharge(error2.times(-1));
		}
		
		//Apply the charges
		for(VectorNode node : space.getNodes()){
			((ChargeableVectorNode)node).applyCharge();
		}
	}

	@Override
	protected Vector randomInitialPosition() {
		return Vector.random(dims);
	}
}
